// 限流装饰器

import 'reflect-metadata';
import { RateLimitManager, RateLimitResult } from '../managers/RateLimitManager';

// 全局限流管理器实例
let globalRateLimitManager: RateLimitManager | null = null;

/**
 * 设置全局限流管理器
 */
export function setRateLimitManager(rateLimitManager: RateLimitManager): void {
  globalRateLimitManager = rateLimitManager;
}

/**
 * 获取全局限流管理器
 */
function getRateLimitManager(): RateLimitManager {
  if (!globalRateLimitManager) {
    throw new Error('RateLimit manager not initialized. Call setRateLimitManager() first.');
  }
  return globalRateLimitManager;
}

export interface RateLimitDecoratorOptions {
  key?: string | ((args: any[]) => string);
  limit: number;
  window: number; // 时间窗口（秒）
  type?: 'fixed' | 'sliding';
  precision?: number; // 滑动窗口精度
  skipSuccessful?: boolean; // 是否跳过成功的请求
  skipFailed?: boolean; // 是否跳过失败的请求
  onLimitExceeded?: (result: RateLimitResult) => void | Promise<void>;
}

/**
 * 限流装饰器
 */
export function RateLimit(options: RateLimitDecoratorOptions) {
  return function (
    target: any,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor
  ): PropertyDescriptor {
    const originalMethod = descriptor.value;

    if (typeof originalMethod !== 'function') {
      throw new Error('@RateLimit can only be applied to methods');
    }

    descriptor.value = async function (...args: any[]) {
      const rateLimitManager = getRateLimitManager();

      // 生成限流键
      const rateLimitKey = generateRateLimitKey(target, propertyKey, args, options.key);

      try {
        // 检查限流
        let result: RateLimitResult;

        if (options.type === 'sliding') {
          result = await rateLimitManager.checkSlidingWindow(
            rateLimitKey,
            options.limit,
            options.window,
            options.precision
          );
        } else {
          result = await rateLimitManager.checkFixedWindow(
            rateLimitKey,
            options.limit,
            options.window
          );
        }

        if (!result.allowed) {
          // 触发限流回调
          if (options.onLimitExceeded) {
            await options.onLimitExceeded(result);
          }

          // 抛出限流错误
          const error = new RateLimitError(`Rate limit exceeded for ${rateLimitKey}`, result);
          throw error;
        }

        // 执行原方法
        let methodResult;
        let methodError;

        try {
          methodResult = await originalMethod.apply(this, args);
        } catch (error) {
          methodError = error;
        }

        // 根据配置决定是否计入限流
        if (options.skipSuccessful && !methodError) {
          // 成功时不计入限流，需要回滚计数
          // 这里简化处理，实际实现可能需要更复杂的逻辑
        }

        if (options.skipFailed && methodError) {
          // 失败时不计入限流，需要回滚计数
          // 这里简化处理
        }

        if (methodError) {
          throw methodError;
        }

        return methodResult;
      } catch (error) {
        if (error instanceof RateLimitError) {
          throw error;
        }

        // 其他错误，根据配置决定是否计入限流
        if (!options.skipFailed) {
          // 失败也计入限流，不做额外处理
        }

        throw error;
      }
    };

    return descriptor;
  };
}

/**
 * 限流错误类
 */
export class RateLimitError extends Error {
  public readonly rateLimitResult: RateLimitResult;

  constructor(message: string, rateLimitResult: RateLimitResult) {
    super(message);
    this.name = 'RateLimitError';
    this.rateLimitResult = rateLimitResult;
  }
}

/**
 * 用户级限流装饰器
 */
export function UserRateLimit(
  options: Omit<RateLimitDecoratorOptions, 'key'> & { userIdIndex?: number }
) {
  const userIdIndex = options.userIdIndex ?? 0;

  return RateLimit({
    ...options,
    key: (args: any[]) => `user:${args[userIdIndex]}`,
  });
}

/**
 * IP级限流装饰器
 */
export function IPRateLimit(
  options: Omit<RateLimitDecoratorOptions, 'key'> & { ipIndex?: number }
) {
  const ipIndex = options.ipIndex ?? 0;

  return RateLimit({
    ...options,
    key: (args: any[]) => `ip:${args[ipIndex]}`,
  });
}

/**
 * 全局限流装饰器
 */
export function GlobalRateLimit(options: Omit<RateLimitDecoratorOptions, 'key'>) {
  return RateLimit({
    ...options,
    key: 'global',
  });
}

/**
 * 方法级限流装饰器
 */
export function MethodRateLimit(options: Omit<RateLimitDecoratorOptions, 'key'>) {
  return RateLimit({
    ...options,
    key: (_args: any[]) => {
      // 键会在装饰器中自动生成
      return '';
    },
  });
}

/**
 * 滑动窗口限流装饰器
 */
export function SlidingWindowRateLimit(options: Omit<RateLimitDecoratorOptions, 'type'>) {
  return RateLimit({
    ...options,
    type: 'sliding',
  });
}

/**
 * 固定窗口限流装饰器
 */
export function FixedWindowRateLimit(options: Omit<RateLimitDecoratorOptions, 'type'>) {
  return RateLimit({
    ...options,
    type: 'fixed',
  });
}

/**
 * 生成限流键
 */
function generateRateLimitKey(
  target: any,
  propertyKey: string | symbol,
  args: any[],
  keyOption?: string | ((args: any[]) => string)
): string {
  if (typeof keyOption === 'function') {
    const customKey = keyOption(args);
    return customKey || generateDefaultKey(target, propertyKey, args);
  }

  if (typeof keyOption === 'string') {
    if (keyOption === 'global') {
      return 'global';
    }

    // 支持模板字符串
    return keyOption.replace(/\$\{args\[(\d+)\]\}/g, (match, index) => {
      const argIndex = parseInt(index, 10);
      return args[argIndex] !== undefined ? String(args[argIndex]) : match;
    });
  }

  return generateDefaultKey(target, propertyKey, args);
}

/**
 * 生成默认键
 */
function generateDefaultKey(target: any, propertyKey: string | symbol, args: any[]): string {
  const className = target.constructor.name;
  const methodName = typeof propertyKey === 'symbol' ? propertyKey.toString() : propertyKey;

  // 如果有参数，使用第一个参数作为标识符
  if (args.length > 0 && args[0] != null) {
    return `${className}:${methodName}:${args[0]}`;
  }

  return `${className}:${methodName}`;
}

/**
 * 限流配置装饰器
 */
export function RateLimitConfig(config: RateLimitDecoratorOptions) {
  return function (_target: any) {
    Reflect.defineMetadata('rateLimit:config', config, _target);
  };
}

/**
 * 获取类的限流配置
 */
export function getRateLimitConfig(target: any): RateLimitDecoratorOptions | undefined {
  return Reflect.getMetadata('rateLimit:config', target);
}

/**
 * 限流键生成器
 */
export const RateLimitKeyGenerators = {
  /**
   * 基于用户ID
   */
  userId:
    (userIdIndex: number = 0) =>
    (args: any[]) =>
      `user:${args[userIdIndex]}`,

  /**
   * 基于IP地址
   */
  ipAddress:
    (ipIndex: number = 0) =>
    (args: any[]) =>
      `ip:${args[ipIndex]}`,

  /**
   * 基于API键
   */
  apiKey:
    (apiKeyIndex: number = 0) =>
    (args: any[]) =>
      `api:${args[apiKeyIndex]}`,

  /**
   * 基于租户ID
   */
  tenantId:
    (tenantIdIndex: number = 0) =>
    (args: any[]) =>
      `tenant:${args[tenantIdIndex]}`,

  /**
   * 组合键
   */
  composite:
    (...generators: Array<(args: any[]) => string>) =>
    (args: any[]) =>
      generators.map(gen => gen(args)).join(':'),

  /**
   * 基于对象属性
   */
  objectProperty:
    (property: string, objectIndex: number = 0) =>
    (args: any[]) => {
      const obj = args[objectIndex];
      return obj?.[property] ? String(obj[property]) : 'unknown';
    },

  /**
   * 基于请求路径
   */
  requestPath:
    (pathIndex: number = 0) =>
    (args: any[]) =>
      `path:${args[pathIndex]}`,

  /**
   * 基于时间窗口
   */
  timeWindow:
    (windowSize: number = 3600) =>
    (_args: any[]) => {
      const now = Math.floor(Date.now() / 1000);
      const window = Math.floor(now / windowSize) * windowSize;
      return `window:${window}`;
    },
};

/**
 * 限流中间件工厂
 */
export function createRateLimitMiddleware(options: RateLimitDecoratorOptions) {
  return async function rateLimitMiddleware(req: any, res: any, next: any) {
    try {
      const rateLimitManager = getRateLimitManager();
      const key =
        typeof options.key === 'function'
          ? options.key([req])
          : (options.key ?? `${req.method}:${req.path}`);

      let result: RateLimitResult;

      if (options.type === 'sliding') {
        result = await rateLimitManager.checkSlidingWindow(
          key,
          options.limit,
          options.window,
          options.precision
        );
      } else {
        result = await rateLimitManager.checkFixedWindow(key, options.limit, options.window);
      }

      // 设置响应头
      res.setHeader('X-RateLimit-Limit', options.limit);
      res.setHeader('X-RateLimit-Remaining', result.remaining);
      res.setHeader('X-RateLimit-Reset', result.resetTime.getTime());

      if (!result.allowed) {
        if (result.retryAfter) {
          res.setHeader('Retry-After', result.retryAfter);
        }

        if (options.onLimitExceeded) {
          await options.onLimitExceeded(result);
        }

        return res.status(429).json({
          error: 'Rate limit exceeded',
          retryAfter: result.retryAfter,
        });
      }

      next();
    } catch (error) {
      next(error);
    }
  };
}
